import wx
import wx.lib.agw.aui as aui
import firebrick
from wx.lib.agw.aui.framemanager import wxEVT_AUI_PERSPECTIVE_CHANGED

ID_WIDGET_INSPECTOR = wx.NewId()
ID_PYCRUST = wx.NewId()

class FirebrickWindow(wx.Frame):
    def __init__(self, *args, **kwargs):
        wx.Frame.__init__(self, *args, **kwargs)

        self.menubar = None

        self._app = wx.GetApp()
        self._panels = {}
        self._pref_dialog = None
        self._scheduled_jobs = {}

        self.auimgr = aui.AuiManager(self)

        wx.EVT_MENU(self, firebrick.ID_SHOW_PREFERENCES, self.OnShowPreferences)
        wx.EVT_MENU(self, firebrick.ID_SAVE_VIEW_AS, self.OnNewPerspective)
        wx.EVT_MENU(self, firebrick.ID_DELETE_CURRENT_PERSPECTIVE, 
                    self.OnDeleteCurrentPerspective)

        self.Bind(aui.EVT_AUI_PANE_CLOSE, self.OnPaneClosed)
        self.Bind(aui.EVT_AUI_PANE_RESTORE, self.OnViewChanged)
        self.Bind(aui.EVT_AUI_PERSPECTIVE_CHANGED, self.OnViewChanged)

    def FindEditor(self):
        '''Return the editor that contains the widget with focus

        Return None if the focused control isn't a child of an 
        editor
        '''
        control = wx.Window.FindFocus()
        while (control is not None and 
               not isinstance(control, firebrick.Editor)):
            control = control.GetParent()
        if isinstance(control, firebrick.Editor):
            return control
        return None
        
    def OnDeleteCurrentPerspective(self, event):
        self.DeleteCurrentPerspective()

    def OnPaneClosed(self, event):
        event.Skip()
        # must be called after the event is processed, or
        # we end up saving the state before the pane goes away
        wx.CallAfter(self.SaveCurrentPerspective)

    def OnViewChanged(self, event):
#        self.SaveCurrentPerspective()
        event.Skip()

    def OnCopyPerspectiveInfo(self, event):
        data = wx.TextDataObject()
        data.SetText(self.auimgr.SavePerspective())
        wx.TheClipboard.Open()
        wx.TheClipboard.SetData(data)
        wx.TheClipboard.Close()
        
    def OnPyCrust(self, event):
        import wx.py
        try:
            self._pycrust.Show()
        except:
            self._pycrust = wx.Frame(None, wx.ID_ANY, "PyCrust")
            wx.py.crust.Crust(self._pycrust)
            self._pycrust.Show()
        
    def OnWidgetInspector(self, event):
        import wx.lib.inspection
        wx.lib.inspection.InspectionTool().Show()
        
    def OnNewPerspective(self, event):
#        for pane in self.auimgr.GetAllPanes():
#            pane.Hide()
        default = self._get_unique_perspective_name("Perspective")
        message = "Name for the new perspective:"
        caption = "New Perspective... - %s" % self._app.title
        dialog = wx.TextEntryDialog(self, message, caption, default, 
                                    style=wx.RESIZE_BORDER|wx.OK|wx.CANCEL)
        if dialog.ShowModal() == wx.ID_OK:
            name = dialog.GetValue()
            self.NewPerspective(name)
            self._app.Publish("perspective.new", perspective=name)
        dialog.Destroy()

    def OnShowPreferences(self, event):
        '''Default handler for firebrick.ID_SHOW_PREFERENCES'''
        self._app.ShowPreferencesDialog(self)

    # def ShowPanel(self, panel_id):
    #     '''Make sure the panel with the given id is shown'''
    #     panel = self.GetPanel(panel_id, create=True)
    #     print "showing", panel_id
    #     self.auimgr.GetPane(panel).Show(True)
    #     self.auimgr.Update()

    def GetToolbar(self, toolbar_id, create=True):
        toolbar = self._app.GetToolbar(self, toolbar_id, create=create)
        return toolbar
    
    def GetPanel(self, panel_id, create=True):
        '''Return a panel for the given panel_id'''
        panel = self._app.GetPanel(self, panel_id, create=create)
#        self.auimgr.GetPane(panel).Show(True)
#        self.auimgr.Update()
        return panel

    def CanDeleteCurrentPerspective(self):
        '''Return True if the current perspective can be deleted'''
        return len(self._app.perspectives) > 1

    def DeleteCurrentPerspective(self):
        '''Delete the current perspective

        This will silently fail if there is only one perspective
        '''
        if self.CanDeleteCurrentPerspective():
            current = self._app.perspectives.pop(0)
            settings = self._app.GetSettings("perspectives")
            del settings[current.name]
            self.LoadPerspective(self._app.perspectives[0].id)
            
    def SaveCurrentPerspective(self):
        '''Save the current perspective info'''
        if self._app.perspectives:
            current_perspective = self._app.perspectives[0]
            self.auimgr.Update() # for good measure
            current_perspective.info = self.auimgr.SavePerspective()
            settings = self._app.GetSettings("perspectives")
            for p in self._app.perspectives:
                if p.name == current_perspective.name:
                    settings[p.name] = p.info
                    self._app.SaveSettings()
                    break

    def AddDebugMenu(self, menu):
        '''Adds a debug menu with items for openign pycrust and the widget inspection tool'''
        debugMenu = wx.Menu()
        debugMenu.Append(ID_WIDGET_INSPECTOR, "Open Widget Inspection Tool")
        debugMenu.Append(ID_PYCRUST, "Open PyCrust")
        debugMenu.Append(firebrick.ID_COPY_PERSPECTIVE_INFO, 
                         "Copy Perspective Info to Clipboard")
        menu.Append(debugMenu, "&Debug")
        wx.EVT_MENU(self, ID_WIDGET_INSPECTOR, self.OnWidgetInspector)
        wx.EVT_MENU(self, ID_PYCRUST, self.OnPyCrust)
        wx.EVT_MENU(self, firebrick.ID_COPY_PERSPECTIVE_INFO, self.OnCopyPerspectiveInfo)
        return debugMenu

    def LoadPerspectiveByName(self, name):
        for index, perspective in enumerate(self._app.perspectives):
            if perspective.name == name:
                # move the perspective to the front of the list,
                # which designates it as the current perspective
                self._app.perspectives.pop(index)
                self._app.perspectives.insert(0, perspective)

                info = perspective.info[:]
                self.LoadPanels(*perspective.panes)
                for pane in self.auimgr.GetAllPanes():
                    pane.Hide()
                self.auimgr.LoadPerspective(perspective.info)
                self.auimgr.Update()
                break
                
                
    def LoadPerspective(self, id_):
        '''ID must be the "id" attribute of a known perspective'''
        for index, perspective in enumerate(self._app.perspectives):
            if perspective.id == id_:

                # move the perspective to the front of the list,
                # which designates it as the current perspective
                self._app.perspectives.pop(index)
                self._app.perspectives.insert(0, perspective)

                info = perspective.info[:]
                self.LoadPanels(*perspective.panes)
                for pane in self.auimgr.GetAllPanes():
                    pane.Hide()
                self.auimgr.LoadPerspective(perspective.info)
                self.auimgr.Update()
                break

    def LoadPanels(self, *args):
        '''Ensure all given panels have been created

        Arguments should be one or more FIDs
        '''
        for wbid in args:
            # we have to go through the app to get the
            # panel; the app will create it if it hasn't
            # yet been created.
            panel = self._app.GetPanel(self, wbid)
            if panel is None:
                panel = self._app.GetToolbar(self, wbid)
            if panel:
                self.auimgr.GetPane(panel).Show()


    def NewPerspective(self, name, panel_ids=[], protected=False):
        '''Create a new perspective
        '''
        
        if name in [perspective.name for perspective in self._app.perspectives]:
            raise firebrick.PerspectiveAlreadyExistsError("perspective '%s' already exists" % name)

        p = firebrick.Perspective(name, protected=protected)
        self._app.perspectives.insert(0,p)
        for pane in self.auimgr.GetAllPanes():
            pane.Hide()

        for panel_id in panel_ids:
            panel = self.GetPanel(panel_id, create=True)
            if not panel:
                # maybe it's a toolbar?
                panel = self.GetToolbar(panel_id, create=True)
            if panel:
                self.auimgr.GetPane(panel).Show()
                
        self.auimgr.Update()
        p.info = self.auimgr.SavePerspective()
        self._app.Publish("perspective.new", perspective=name)

    def _get_unique_perspective_name(self, prefix):
        unique_name = prefix
        count = 0
        current_names = [p.name for p in self._app.perspectives]
        while unique_name in current_names:
            count += 1
            unique_name = "%s #%s" % (prefix, count)
        return unique_name

    def _schedule_job(self, func, *args):
        '''Run a job, or restart an already existing scheduled job

        This is useful for running a single job that might be 
        triggered by several events
        '''
        key = (func,) + args
        if key in self._scheduled_jobs and self._scheduled_jobs[key].IsRunning():
            self._scheduled_jobs[key].Restart()
        else:
            self._scheduled_jobs[key] = wx.CallLater(100, func, *args)

